package com.lancoo.edu.platform.operation.backupRestore.service.bo;

import ch.qos.logback.classic.LoggerContext;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.lancoo.edu.platform.operation.backupRestore.constant.BackupRestoreConstant;
import com.lancoo.edu.platform.operation.backupRestore.constant.DataBaseTypeEnum;
import com.lancoo.edu.platform.operation.backupRestore.constant.DateBaseStrategyEnum;
import com.lancoo.edu.platform.operation.backupRestore.domain.po.BrBackupInfo;
import com.lancoo.edu.platform.operation.backupRestore.domain.po.BrServerSysInfo;
import com.lancoo.edu.platform.operation.backupRestore.domain.po.BrSysDatabaseInfo;
import com.lancoo.edu.platform.operation.backupRestore.service.po.BrBackupInfoService;
import com.lancoo.edu.platform.operation.backupRestore.service.po.BrServerSysInfoService;
import com.lancoo.edu.platform.operation.backupRestore.service.po.BrSysDatabaseInfoService;
import com.lancoo.edu.platform.operation.backupRestore.strategy.backupRestore.BackupRestoreStrategy;
import com.lancoo.edu.platform.operation.backupRestore.strategy.backupRestore.imp.DmBackupRestoreStrategy;
import com.lancoo.edu.platform.operation.backupRestore.strategy.backupRestore.imp.MysqlBackupRestoreStrategy;
import com.lancoo.edu.platform.operation.backupRestore.strategy.backupRestore.imp.OceanBaseBackupRestoreStrategy;
import com.lancoo.edu.platform.operation.commons.config.DatabaseConfig;
import com.lancoo.edu.platform.operation.commons.config.LancooConfig;
import com.lancoo.edu.platform.operation.commons.exception.ServiceException;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lzh
 * @Date 2024/3/25 11:44
 * @description 数据恢复业务逻辑
 */
@Service
@AllArgsConstructor
public class BrBackupBoService {

    private final BrSysDatabaseInfoService brSysDatabaseInfoService;
    private final BrServerSysInfoService brServerSysInfoService;
    private final LancooConfig lancooConfig;
    private final DatabaseConfig databaseConfig;
    private final BrBackupInfoService brBackupInfoService;

    /**
     * 主动备份功能
     *
     * @param eduId    教育局ID
     * @param sysId       系统ID
     */
    // public void backup(String eduId, String sysId) {
    //     // 1、查询需要备份的系统对应数据库列表 和 系统对应服务器信息列表
    //     BrSysDatabaseInfo brSysDatabaseInfo = brSysDatabaseInfoService.getInfo(sysId);
    //     BrServerSysInfo brServerSysInfo = brServerSysInfoService.getInfoBySysId(eduId, sysId);
    //     if (Objects.isNull(brSysDatabaseInfo) || Objects.isNull(brServerSysInfo)) {
    //         return;
    //     }
    //     // 2、备份
    //     String savePath = lancooConfig.getBackSavePath() + "/" + sysId + "/" + eduId + "/";
    //     String database = String.join(",", brSysDatabaseInfo.getDatabaseNameList());
    //     database = database.replace("[eduId]", eduId);
    //     String databaseType = lancooConfig.getDatabaseType();
    //     BackupRestoreStrategy backupRestoreStrategy = null;
    //     String databaseDump = null;
    //     switch (DataBaseTypeEnum.getByType(databaseType)) {
    //         case MYSQL:
    //             databaseDump = DataBaseTypeEnum.MYSQL.getDatabaseDump();
    //             backupRestoreStrategy = new MysqlBackupRestoreStrategy();
    //             break;
    //         case DM:
    //             databaseDump = DataBaseTypeEnum.DM.getDatabaseDump();
    //             backupRestoreStrategy = new DmBackupRestoreStrategy();
    //             break;
    //         case OCEAN_BASE:
    //             databaseDump = DataBaseTypeEnum.OCEAN_BASE.getDatabaseDump();
    //             backupRestoreStrategy = new OceanBaseBackupRestoreStrategy();
    //             break;
    //         default:
    //             break;
    //     }
    //     if (Objects.isNull(backupRestoreStrategy) || Objects.isNull(databaseDump)){
    //         throw new ServiceException("备份失败");
    //     }
    //     String filePath = backupRestoreStrategy.backup(sysId, database, savePath, databaseDump, databaseConfig.getUserName(), databaseConfig.getPassword(), brServerSysInfo.getServerIp(), databaseConfig.getPort());
    //     // 3、保存备份记录
    //     BrBackupInfo brBackupInfo = new BrBackupInfo();
    //     brBackupInfo.setBackupTime(new Date());
    //     brBackupInfo.setDatabaseName(database);
    //     File file = new File(filePath);
    //     try {
    //         brBackupInfo.setBackupFilePath(file.getCanonicalPath());
    //     } catch (IOException e) {
    //         throw new RuntimeException(e);
    //     }
    //     brBackupInfo.setSysId(sysId);
    //     brBackupInfo.setEduId(eduId);
    //     brBackupInfoService.save(brBackupInfo);
    // }

    /**
     * 主动备份功能
     *
     * @param eduId    教育局ID
     * @param sysId       系统ID
     */
    public void backupSingle(String eduId, String sysId) {
        // 1、查询需要备份的系统对应数据库列表 和 系统对应服务器信息列表
        BrSysDatabaseInfo brSysDatabaseInfo = brSysDatabaseInfoService.getInfo(sysId);
        BrServerSysInfo brServerSysInfo = brServerSysInfoService.getInfoBySysId(eduId, sysId);
        if (Objects.isNull(brSysDatabaseInfo) || Objects.isNull(brServerSysInfo)) {
            throw new ServiceException("备份失败，未获取到系统数据库IP");
        }
        // 2、备份
        String savePath = lancooConfig.getBackSavePath() + "/" + sysId + "/" + eduId + "/";
        String database = String.join(",", brSysDatabaseInfo.getDatabaseNameList());
        database = database.replace("[eduId]", eduId);
        String databaseType = lancooConfig.getDatabaseType();
        String systemEnv = lancooConfig.getSystemEnv();
        // 2.1、获取对应的策略
        DateBaseStrategyEnum dateBaseStrategyEnum = DateBaseStrategyEnum.getStrategy(databaseType);
        BackupRestoreStrategy backupRestoreStrategy = dateBaseStrategyEnum.getBackupRestoreStrategy();
        String databaseDump = DataBaseTypeEnum.getByType(databaseType, systemEnv).getDatabaseDump();
        // 2.2、数据备份之后的文件地址
        String filePath = backupRestoreStrategy.backup(sysId, database, savePath, databaseDump, databaseConfig.getUserName(), databaseConfig.getPassword(), brServerSysInfo.getServerIp(), databaseConfig.getPort());
        // 3、保存备份记录
        Long backupUniqueId = IdWorker.getId();
        String backupUniqueDate = DateUtil.format(new Date(), "yyyy-MM-dd");
        BrBackupInfo brBackupInfo = getBrBackupInfo(eduId, database, backupUniqueId, backupUniqueDate, sysId, filePath, BackupRestoreConstant.BACKUP_TYPE_SINGLE);
        brBackupInfoService.save(brBackupInfo);
    }

    /**
     * 备份方法
     *
     * @param eduId    教育局ID
     */
    public void backupAll(String eduId) {
        String databaseType = lancooConfig.getDatabaseType();
        String systemEnv = lancooConfig.getSystemEnv();
        DateBaseStrategyEnum dateBaseStrategyEnum = DateBaseStrategyEnum.getStrategy(databaseType);
        BackupRestoreStrategy backupRestoreStrategy = dateBaseStrategyEnum.getBackupRestoreStrategy();
        String databaseDump = DataBaseTypeEnum.getByType(databaseType, systemEnv).getDatabaseDump();
        // 1、查询需要备份的系统对应数据库列表 和 系统对应服务器信息列表
        List<BrSysDatabaseInfo> brSysDatabaseInfoList = brSysDatabaseInfoService.getListDistinct();
        List<BrServerSysInfo> brServerSysInfoList = brServerSysInfoService.getListByEduId(eduId);
        if (CollectionUtil.isEmpty(brSysDatabaseInfoList) || CollectionUtil.isEmpty(brServerSysInfoList)) {
            return;
        }
        Map<String, String> sysMapDatabase = brSysDatabaseInfoList.stream().collect(Collectors.groupingBy(BrSysDatabaseInfo::getSysId,
                Collectors.mapping(BrSysDatabaseInfo::getDatabaseNameList, Collectors.mapping(list -> String.join(",", list), Collectors.joining()))));
        Map<String, String> sysMapServer = brServerSysInfoList.stream().collect(Collectors.groupingBy(BrServerSysInfo::getSysId, Collectors.mapping(BrServerSysInfo::getServerIp, Collectors.joining())));
        // 2、循环需要备份的子系统
        List<BrBackupInfo> backupInfoList = new ArrayList<>();
        Long backupUniqueId = IdWorker.getId();
        String backupUniqueDate = DateUtil.format(new Date(), "yyyy-MM-dd");
        for (Map.Entry<String, String> entry : sysMapServer.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (sysMapDatabase.containsKey(key)) {
                String savePath = lancooConfig.getBackSavePath() + "/" + key + "/" + eduId + "/";
                String database = sysMapDatabase.get(key);
                database = database.replace("[eduId]", eduId);
                String filePath = backupRestoreStrategy.backup(key, database, savePath, databaseDump, databaseConfig.getUserName(), databaseConfig.getPassword(), value, databaseConfig.getPort());
                BrBackupInfo brBackupInfo = getBrBackupInfo(eduId, sysMapDatabase.get(key), backupUniqueId, backupUniqueDate, key, filePath, BackupRestoreConstant.BACKUP_TYPE_ALL);
                backupInfoList.add(brBackupInfo);
            }
        }
        // 3、保存备份记录
        brBackupInfoService.saveBatch(backupInfoList);
    }

    /**
     * 现在正确数据库类型备份
     *
     * @param databaseType 数据库类型
     * @param eduId     教育局ID
     */
    // @Transactional(rollbackFor = Exception.class)
    // public void selectTypeBackup(String databaseType, String eduId) {
    //     switch (DataBaseTypeEnum.getByType(databaseType)) {
    //         case MYSQL:
    //             MysqlBackupRestoreStrategy mysqlBackupStrategy = new MysqlBackupRestoreStrategy();
    //             backup(mysqlBackupStrategy, eduId, DataBaseTypeEnum.MYSQL.getDatabaseDump());
    //             break;
    //         case DM:
    //             DmBackupRestoreStrategy dmBackupStrategy = new DmBackupRestoreStrategy();
    //             backup(dmBackupStrategy, eduId, DataBaseTypeEnum.DM.getDatabaseDump());
    //             break;
    //         case OCEAN_BASE:
    //             OceanBaseBackupRestoreStrategy oceanBaseBackupStrategy = new OceanBaseBackupRestoreStrategy();
    //             backup(oceanBaseBackupStrategy, eduId, DataBaseTypeEnum.OCEAN_BASE.getDatabaseDump());
    //             break;
    //         default:
    //             break;
    //     }
    // }

    /**
     * 备份方法
     *
     * @param backupRestoreStrategy 备份策略
     * @param eduId              教育局ID
     * @param databaseDump          数据库的bin目录
     */
    public void backup(BackupRestoreStrategy backupRestoreStrategy, String eduId, String databaseDump) {
        // 1、查询需要备份的系统对应数据库列表 和 系统对应服务器信息列表
        List<BrSysDatabaseInfo> brSysDatabaseInfoList = brSysDatabaseInfoService.getListDistinct();
        List<BrServerSysInfo> brServerSysInfoList = brServerSysInfoService.getListByEduId(eduId);
        if (CollectionUtil.isEmpty(brSysDatabaseInfoList) || CollectionUtil.isEmpty(brServerSysInfoList)) {
            return;
        }
        Map<String, String> sysMapDatabase = brSysDatabaseInfoList.stream().collect(Collectors.groupingBy(BrSysDatabaseInfo::getSysId,
                Collectors.mapping(BrSysDatabaseInfo::getDatabaseNameList, Collectors.mapping(list -> String.join(",", list), Collectors.joining()))));
        Map<String, String> sysMapServer = brServerSysInfoList.stream().collect(Collectors.groupingBy(BrServerSysInfo::getSysId, Collectors.mapping(BrServerSysInfo::getServerIp, Collectors.joining())));
        // 2、循环需要备份的子系统
        List<BrBackupInfo> backupInfoList = new ArrayList<>();
        for (Map.Entry<String, String> entry : sysMapServer.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            String filePath = null;
            if (sysMapDatabase.containsKey(key)) {
                String savePath = lancooConfig.getBackSavePath() + "/" + key + "/" + eduId + "/";
                String database = sysMapDatabase.get(key);
                database = database.replace("[eduId]", eduId);
                filePath = backupRestoreStrategy.backup(key, database, savePath, databaseDump, databaseConfig.getUserName(), databaseConfig.getPassword(), value, databaseConfig.getPort());
            }
            if (StringUtils.isBlank(filePath)) {
                continue;
            }
            BrBackupInfo brBackupInfo = new BrBackupInfo();
            brBackupInfo.setBackupTime(new Date());
            brBackupInfo.setDatabaseName(sysMapDatabase.get(key));
            File file = new File(filePath);
            try {
                brBackupInfo.setBackupFilePath(file.getCanonicalPath());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            brBackupInfo.setSysId(key);
            brBackupInfo.setEduId(eduId);
            backupInfoList.add(brBackupInfo);
        }
        // 3、保存备份记录
        brBackupInfoService.saveBatch(backupInfoList);
    }

    /**
     * 获取备份记录实体
     *
     * @param eduId            教育局ID
     * @param databaseName     数据库名称
     * @param backupUniqueId   备份唯一ID
     * @param backupUniqueDate 备份唯一ID对应的日期
     * @param sysId            系统ID
     * @param filePath         备份文件地址
     * @param backupType       备份类型。1全部，2单个
     * @return 备份记录实体
     */
    private static BrBackupInfo getBrBackupInfo(String eduId, String databaseName, Long backupUniqueId, String backupUniqueDate, String sysId, String filePath, Integer backupType) {
        BrBackupInfo brBackupInfo = new BrBackupInfo();
        brBackupInfo.setBackupTime(new Date());
        brBackupInfo.setBackupUniqueId(backupUniqueId);
        brBackupInfo.setDatabaseName(databaseName);
        brBackupInfo.setBackupUniqueDate(backupUniqueDate);
        String backupFilePath = filePath;
        if (!Objects.equals(filePath, BackupRestoreConstant.ERROR_BOOLEAN)) {
            try {
                File file = new File(filePath);
                backupFilePath = file.getCanonicalPath();
            } catch (IOException e) {
                throw new ServiceException("备份失败，请联系管理员");
            }
        }
        brBackupInfo.setBackupFilePath(backupFilePath);
        brBackupInfo.setSysId(sysId);
        brBackupInfo.setEduId(eduId);
        brBackupInfo.setBackupType(backupType);
        return brBackupInfo;
    }
}
